package com.elminster.util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CSVUtil {

    /** encoding. */
    protected static final String ENCODING = "UTF-8";

    @SuppressWarnings("unchecked")
    protected Vector items;

    @SuppressWarnings("unchecked")
    public CSVUtil() {
        items = new Vector();
    }

    public final void addItem(Object item) {
        if (item == null) {
            item = "";
        }
        if (item instanceof String) {
            addItem(item, false);
        } else {
            addItem(String.valueOf(item), false);
        }
    }

    @SuppressWarnings("unchecked")
    public final void addItem(Object item, boolean enquote) {

        if (item == null) {
            item = "";
        }
        if (item instanceof String) {
            items.addElement(new Element((String) item, enquote));
        } else {
            items.addElement(new Element(String.valueOf(item), enquote));
        }

    }

    public String getLine() {
        StringBuffer list = new StringBuffer();
        for (int n = 0; n < items.size(); n++) {
            Element element = (Element) items.elementAt(n);
            String item = element.getItem();
            list.append(item);
            if (items.size() - 1 != n) {
                list.append(',');
            }
        }
        list.append("\r\n");
        items.clear();
        return new String(list);
    }

    public final int size() {
        return items.size();
    }

    public final String getItem(int n) {
        Element element = (Element) items.elementAt(n);
        return element.getRawItem();
    }

    public final void removeItem(int n) {
        items.removeElementAt(n);
    }

    @SuppressWarnings("unchecked")
    public final Enumeration elements() {
        return new CSVLineEnumerator(items);
    }

    public void write(String file) throws Exception {
        File f = new File(file);
        if (f.getParent() != null) {
            new File(f.getParent()).mkdirs();
        }
        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                        new FileOutputStream(file, true), ENCODING)));
        out.print(this.getLine());
        out.flush();
        out.close();
    }

    class Element {
        private String item;
        private boolean enquote;

        Element(String item) {
            this(item, false);
        }

        Element(String item, boolean enquote) {
            this.item = item;
            this.enquote = enquote;
        }

        public String getItem() {
            return enquote(item, enquote);
        }

        public String getRawItem() {
            return item;
        }
    }

    @SuppressWarnings("unchecked")
    class CSVLineEnumerator implements Enumeration {
        private Vector items;
        private int n;

        CSVLineEnumerator(Vector items) {
            this.items = items;
            n = 0;
        }

        public Object nextElement() {
            n++;
            Element element = (Element) items.elementAt(n - 1);
            return element.getRawItem();
        }

        public boolean hasMoreElements() {
            return n < items.size();
        }
    }

    public static String enquote(String item) {
        return enquote(item, false);
    }

    public static String enquote(String item, boolean enquote) {
        if (item.length() == 0) {
            if (enquote) {
                return "\"\"";
            } else {
                return item;
            }
        }

        if (item.indexOf('\r') < 0
                && item.indexOf('\n') < 0
                && item.indexOf('"') < 0
                && item.indexOf(',') < 0
                && !enquote) {
            return item;
        }

        StringBuffer sb = new StringBuffer(item.length() * 2 + 2);
        sb.append('"');
        for (int ind = 0; ind < item.length(); ind++) {
            char ch = item.charAt(ind);
            if ('"' == ch) {
                sb.append("\"\"");
            } else {
                sb.append(ch);
            }
        }
        sb.append('"');

        return new String(sb);
    }

    public static String enquoteForLIB(String item) {
        if (item.length() == 0) { return item; }
        if (item.indexOf('"') < 0 && item.indexOf(',') < 0) {
            return item;
        }

        StringBuffer sb = new StringBuffer();
        for (int ind = 0; ind < item.length(); ind++) {
            char ch = item.charAt(ind);
            if (('"' != ch) && (',' != ch)) {
                sb.append(ch);
            }
        }

        return sb.toString();
    }

    public static void write(String file, String[] csvLines) throws Exception {
        File f = new File(file);
        if (f.getParent() != null) {
            new File(f.getParent()).mkdirs();
        }
        PrintWriter out = null;
        out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                new FileOutputStream(file, true), ENCODING)));
        for (int i = 0; i < csvLines.length; i++) {
            out.print(csvLines[i]);
            out.print("\r\n");
        }
        out.flush();
        out.close();
    }

    @SuppressWarnings("unchecked")
    public static List split(String target, String delimiter) {
        List result = new ArrayList();
        int pos = 0;
        String temp = target;
        while ((pos = temp.indexOf(delimiter)) != -1) {
            result.add(temp.substring(0, pos));
            temp = temp.substring(pos + 1);
        }
        if (target.endsWith(delimiter)) {
            result.add("");
        } else {
            result.add(temp);
        }
        return result;
    }
    
    @SuppressWarnings("unchecked")
    public static List<String> split(String str) {
        List<String> list = new ArrayList<String>();
        
        // .*".+".*
        String pattern = ".*\".+\".*";
        Pattern p = Pattern.compile(pattern);
        Matcher matcher = p.matcher(str);
        
        boolean special = matcher.find();
        
        if (special) {
            // sign stack
            Stack<String> signStack = new Stack<String>();
            // String queue
            Queue<String> stringQueue = new LinkedList<String>();
            
            boolean closed = true;
            
            // remove "/r", "/n"
            str = str.replace("\r", "").replace("\n", "");
            
            int length = str.length();

            StringBuilder string = new StringBuilder();
            for (int i = 0; i < length; i++) {
                char c = str.charAt(i);
                
                if (closed && ',' == c) {
                    // ","
                    // enqueue to the string
                    stringQueue.offer(string.toString());
                    // reset the string
                    string = new StringBuilder();
                } else if ('\"' == c) {
                    // """
                    if (closed) {
                        // set the close flag
                        closed = false;
                        // push to the stack
                        signStack.push("#");
                        // do nothing
                    } else {
                        // unclosed
                        if (length == i + 1 || ',' == str.charAt(i + 1)) {
                            // last character or following a ","
                            // reset the close flag
                            closed = true;
                            // pop from the stack
                            signStack.pop();
                            // do nothing
                        } else {
                            // other condition
                            // ignore the """
                            // add the next character to the string, and skip it
                            string.append(str.charAt(++i));
                            // do nothing with the flag and the stack
                        }
                    }
                } else {
                    // add to the string
                    string.append(c);
                }
                
            } // end for
            
            // last string is left, add into queue
            stringQueue.add(string.toString());
            
            // analyze fail
            if (!closed || !signStack.isEmpty()) {
                return null;
            } else {
                // insert into the return list
                list.addAll(stringQueue);
            }
        } else {
            list = CSVUtil.split(str, ",");
        }
        
        return list;
    }
}
